/*************************************************************************
 *                                                                       *
 * This file is part of the DogosFrame distribution.  For full copyright *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 *                                                                       *
 *************************************************************************/

#ifndef DOGOS_include_COMMON_base_DogosExecptions_h
#define DOGOS_include_COMMON_base_DogosExecptions_h

#include "DogosConfig.h"

#ifdef DOGOS_ENABLE_EXCEPTIONS
#include <stdexcept>
#include <string>
#include <sstream>

#ifdef DOGOS_HAVE_CXX11_NOEXCEPT
#define TBOX_NOEXCEPT noexcept
#else
#define TBOX_NOEXCEPT throw()
#endif

namespace DOGOS
{

/**
 * A class to represent the internal "this should never happen"
 * errors, to be thrown by "libmesh_error();"
 */
class LogicError : public std::logic_error
{
public:
    LogicError() : std::logic_error( "Error in libMesh internal logic" ) {}
};


/**
 * A class to stub for features that should be in libMesh, but
 * haven't been written yet, to be thrown by
 * "libmesh_not_implemented();"
 */
class NotImplemented : public std::logic_error
{
public:
    NotImplemented() : std::logic_error( "Error: not implemented!" ) {}
};


/**
 * A class representing a failed attempt by the library to open a
 * file (or construct an fstream, etc), to be thrown by
 * "libmesh_file_error(filename);" For ease of debugging, "filename"
 * should include any (absolute or relative or implicit) pathname
 * that was part of the failed open.
 */
class FileError : public std::runtime_error
{
public:
    FileError(const std::string & filename) : std::runtime_error( "Error accessing file: " + filename ) {}
};


/**
 * A class representing a solver's failure to converge, to be thrown
 * by "TBOX_CONVERGENCE_FAILURE();"  This should be a last
 * resort; more often, a solve which has failed should be
 * reattempted after switching to a smaller timestep, adding
 * underrelaxation, taking a smaller continuation step, etc.
 */
class ConvergenceFailure : public std::runtime_error
{
public:
    ConvergenceFailure() : std::runtime_error( "Unrecoverable failure to converge" ) {}
};


/**
 * A class representing that a dynamic cast failed to produce expected output.
 */
class DynamicCastFailure:  public std::runtime_error
{
public:
    DynamicCastFailure() : std::runtime_error( "Failed dynamic cast!" ) {}
};

/**
 * A class representing a floating point exception.
 */
class FloatingPointException: public std::runtime_error
{
public:
    FloatingPointException() : std::runtime_error( "libmesh FPE!" ) {}
};

/**
 * A class representing an exception during a solve.
 */
class SolverException: public std::exception
{
public:
    SolverException(int error_code_in) :
        std::exception(),
        error_code(error_code_in)
    {
        std::ostringstream oss;
        oss << "Error code " << error_code << " during solve." << std::endl;
        what_message = oss.str();
    }

    /**
     * Virtual destructor, gotta have one of those.
     */
    virtual ~SolverException() TBOX_NOEXCEPT {};

    /**
     * Override the what() function to provide a generic error message.
     */
    virtual const char * what() const TBOX_NOEXCEPT
    {
        // std::string::c_str() is noexcept in C++11, so it's safe to call
        // in what() because it can't throw.
        return what_message.c_str();
    }

    /**
     * The error code generated by the solver.
     */
    int error_code;

    /**
     * string which holds the message built in the constructor.
     */
    std::string what_message;
};

}

#define TBOX_THROW(e) do { throw e; } while (0)
#define TBOX_TRY try
#define TBOX_CATCH(e) catch(e)

#else

#define TBOX_THROW(e) do { std::abort(); } while (0)
#define TBOX_TRY
#define TBOX_CATCH(e) if (0)

#endif // TBOX_ENABLE_EXCEPTIONS

#endif // TBOX_TBOX_EXCEPTIONS_H
